XML Processing disable external
September 02, 2020
最近用Sonar扫描老代码的时候,发现了一个blocker级别的问题,就是xml在处理之前,生成的Facotry必须声明Disable外部实体。否则会有安全风险,具体如下。
问题
return DocumentBuilderFactory.newInstance().newDocumentBuilder();
上面的代码就没有配置,直接用的默认设置,这就是问题根源。根据Sonar Rule,这个问题有如下表现形式:
// DocumentBuilderFactory library:
String xml = "xxe.xml";
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance(); // Noncompliant
DocumentBuilder builder = df.newDocumentBuilder();
Document document = builder.parse(new InputSource(xml));
DOMSource domSource = new DOMSource(document);
// SAXParserFactory library:
String xml = "xxe.xml";
SaxHandler handler = new SaxHandler();
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser(); // Noncompliant
parser.parse(xml, handler);
// XMLInputFactory library:
XMLInputFactory factory = XMLInputFactory.newInstance(); // Noncompliant
XMLEventReader eventReader = factory.createXMLEventReader(new FileReader("xxe.xml"));
// TransformerFactory library:
String xslt = "xxe.xsl";
String xml = "xxe.xml";
TransformerFactory transformerFactory = javax.xml.transform.TransformerFactory.newInstance(); // Noncompliant
Transformer transformer = transformerFactory.newTransformer(new StreamSource(xslt));
StringWriter writer = new StringWriter();
transformer.transform(new StreamSource(xml), new StreamResult(writer));
String result = writer.toString();
// SchemaFactory library:
String xsd = "xxe.xsd";
StreamSource xsdStreamSource = new StreamSource(xsd);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); // Noncompliant
Schema schema = schemaFactory.newSchema(xsdStreamSource);
// Validator library:
String xsd = "xxe.xsd";
String xml = "xxe.xml";
StreamSource xsdStreamSource = new StreamSource(xsd);
StreamSource xmlStreamSource = new StreamSource(xml);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(xsdStreamSource);
Validator validator = schema.newValidator(); // Noncompliant
StringWriter writer = new StringWriter();
validator.validate(xmlStreamSource, new StreamResult(writer));
//Dom4j library:
SAXReader xmlReader = new SAXReader(); // Noncompliant by default
Document xmlResponse = xmlReader.read(xml);
// Jdom2 library:
SAXBuilder builder = new SAXBuilder(); // Noncompliant by default
Document document = builder.build(new File(xml));
解决问题
解决也很简单,就是要修改两个默认配置: XMLConstants.ACCESS_EXTERNAL_DTD 以及 XMLConstants.ACCESS_EXTERNAL_SCHEMA
// DocumentBuilderFactory library:
String xml = "xxe.xml";
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // compliant
DocumentBuilder builder = df.newDocumentBuilder();
Document document = builder.parse(new InputSource(xml));
DOMSource domSource = new DOMSource(document);
// SAXParserFactory library:
String xml = "xxe.xml";
SaxHandler handler = new SaxHandler();
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant
parser.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // compliant
parser.parse(xml, handler);
// XMLInputFactory library:
XMLInputFactory factory = XMLInputFactory.newInstance();
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // compliant
XMLEventReader eventReader = factory.createXMLEventReader(new FileReader("xxe.xml"));
// TransformerFactory library:
String xslt = "xxe.xsl";
String xml = "xxe.xml";
TransformerFactory transformerFactory = javax.xml.transform.TransformerFactory.newInstance();
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); // Compliant
// ACCESS_EXTERNAL_SCHEMA not supported in several TransformerFactory implementations
Transformer transformer = transformerFactory.newTransformer(new StreamSource(xslt));
StringWriter writer = new StringWriter();
transformer.transform(new StreamSource(xml), new StreamResult(writer));
String result = writer.toString();
// SchemaFactory library:
String xsd = "xxe.xsd";
StreamSource xsdStreamSource = new StreamSource(xsd);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // Compliant
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant
Schema schema = schemaFactory.newSchema(xsdStreamSource);
// Validator library:
String xsd = "xxe.xsd";
String xml = "xxe.xml";
StreamSource xsdStreamSource = new StreamSource(xsd);
StreamSource xmlStreamSource = new StreamSource(xml);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(xsdStreamSource);
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
// validators will also inherit of these properties
Validator validator = schema.newValidator();
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // Compliant
StringWriter writer = new StringWriter();
validator.validate(xmlStreamSource, new StreamResult(writer));
// For dom4j library, ACCESS_EXTERNAL_DTD and ACCESS_EXTERNAL_SCHEMA are not supported, thus a very strict fix is to disable
// doctype declarations:
SAXReader xmlReader = new SAXReader();
xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); // Compliant
Document xmlResponse = xmlReader.read(xml);
// Jdom2 library:
SAXBuilder builder = new SAXBuilder(); // Compliant
builder.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant
builder.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // Compliant
Document document = builder.build(new File(xml));
还有一种说法:
This rule raises an issue when any of the following are used without first disabling external entity processing: javax.xml.validation.Validator, JAXP’s DocumentBuilderFactory, SAXParserFactory, Xerces 1 and Xerces 2 StAX’s XMLInputFactory and XMLReaderFactory.
To disable external entity processing for XMLInputFactory, configure one of the properties XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES or XMLInputFactory.SUPPORT_DTD to false.
To disable external entity processing for SAXParserFactory, XMLReader or DocumentBuilderFactory configure one of the features XMLConstants.FEATURE_SECURE_PROCESSING or ”http://apache.org/xml/features/disallow-doctype-decl” to true.
To disable external entity processing for Validator, configure both properties XMLConstants.ACCESS_EXTERNAL_DTD, XMLConstants.ACCESS_EXTERNAL_SCHEMA to the empty string "".
其他库
XMLStants是JDK 1.5(JAXP 1.3)引入的,而上面两个新属性是JDK 1.7(JAXP 1.5. JDK7u40)引入的。很多老的库不支持,比如以下是项目中的两个例子:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.15</version>
<exclusions>
<exclusion>
<artifactId>xml-apis</artifactId>
<groupId>xml-apis</groupId>
</exclusion>
<!-- as this old XMLConstants duplicate JDK 1.8 -->
<exclusion>
<groupId>stax</groupId>
<artifactId>stax-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>2.2.1.GA</version>
<exclusions>
<exclusion>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
</exclusion>
<!-- as this old XMLConstants duplicate JDK 1.8 -->
<exclusion>
<groupId>javax.xml.stream</groupId>
<artifactId>stax-api</artifactId>
</exclusion>
</exclusions>
</dependency>
资源
1. https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html